linux-servic脚本分析

NULL

之前,打了一个很有意思的靶机,里面审了linux里的service,超有意思

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#!/bin/sh

###########################################################################
# /usr/bin/service
#
# A convenient wrapper for the /etc/init.d init scripts.
#
# This script is a modified version of the /sbin/service utility found on
# Red Hat/Fedora systems (licensed GPLv2+).
#
# Copyright (C) 2006 Red Hat, Inc. All rights reserved.
# Copyright (C) 2008 Canonical Ltd.
# * August 2008 - Dustin Kirkland <kirkland@canonical.com>
# Copyright (C) 2013 Michael Stapelberg <stapelberg@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# On Debian GNU/Linux systems, the complete text of the GNU General
# Public License can be found in `/usr/share/common-licenses/GPL-2'.
###########################################################################


is_ignored_file() {
case "$1" in
skeleton | README | *.dpkg-dist | *.dpkg-old | rc | rcS | single | reboot | bootclean.sh)
return 0
;;
esac
return 1
}

VERSION="`basename $0` ver. 1.57+kali1"
USAGE="Usage: `basename $0` < option > | --status-all | \
[ service_name [ command | --full-restart ] ]"
SERVICE=
ACTION=
SERVICEDIR="/etc/init.d"
OPTIONS=
is_systemd=


if [ $# -eq 0 ]; then
echo "${USAGE}" >&2
exit 1
fi

if [ -d /run/systemd/system ]; then
is_systemd=1
fi

cd /
while [ $# -gt 0 ]; do

if [ -z "${SERVICE}" -a $# -eq 1 -a "${1}" = "--status-all" ]; then
cd ${SERVICEDIR}
for SERVICE in * ; do
case "${SERVICE}" in
functions | halt | killall | single| linuxconf| kudzu)
;;
*)
if ! is_ignored_file "${SERVICE}" \
&& [ -x "${SERVICEDIR}/${SERVICE}" ]; then
out=$(env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" status 2>&1)
retval=$?
if echo "$out" | egrep -iq "usage:"; then
#printf " %s %-60s %s\n" "[?]" "$SERVICE:" "unknown" 1>&2
echo " [ ? ] $SERVICE" 1>&2
continue
else
if [ "$retval" = "0" -a -n "$out" ]; then
#printf " %s %-60s %s\n" "[+]" "$SERVICE:" "running"
echo " [ + ] $SERVICE"
continue
else
#printf " %s %-60s %s\n" "[-]" "$SERVICE:" "NOT running"
echo " [ - ] $SERVICE"
continue
fi
fi
#env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" status
fi
;;
esac
done
exit 0
elif [ $# -eq 2 -a "${2}" = "--full-restart" ]; then
SERVICE="${1}"
# On systems using systemd, we just perform a normal restart:
# A restart with systemd is already a full restart.
if [ -n "$is_systemd" ]; then
ACTION="restart"
else
if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" stop
env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" start
exit $?
fi
fi
elif [ -z "${SERVICE}" ]; then
SERVICE="${1}"
elif [ -z "${ACTION}" ]; then
ACTION="${1}"
else
OPTIONS="${OPTIONS} ${1}"
fi
shift
;;
esac
done

run_via_sysvinit() {
# Otherwise, use the traditional sysvinit
if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
exec env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" ${ACTION} ${OPTIONS}
else
echo "${SERVICE}: unrecognized service" >&2
exit 1
fi
}

update_openrc_started_symlinks() {
# maintain the symlinks of /run/openrc/started so that
# rc-status works with the service command as well
if [ -d /run/openrc/started ] ; then
case "${ACTION}" in
start)
if [ ! -h /run/openrc/started/$SERVICE ] ; then
ln -s $SERVICEDIR/$SERVICE /run/openrc/started/$SERVICE || true
fi
;;
stop)
rm /run/openrc/started/$SERVICE || true
;;
esac
fi
}

# When this machine is running systemd, standard service calls are turned into
# systemctl calls.
if [ -n "$is_systemd" ]
then
UNIT="${SERVICE%.sh}.service"
# avoid deadlocks during bootup and shutdown from units/hooks
# which call "invoke-rc.d service reload" and similar, since
# the synchronous wait plus systemd's normal behaviour of
# transactionally processing all dependencies first easily
# causes dependency loops
if ! systemctl --quiet is-active multi-user.target; then
sctl_args="--job-mode=ignore-dependencies"
fi

case "${ACTION}" in
restart|status|try-restart)
exec systemctl $sctl_args ${ACTION} ${UNIT}
;;
start|stop)
# Follow the principle of least surprise for SysV people:
# When running "service foo stop" and foo happens to be a service that
# has one or more .socket files, we also stop the .socket units.
# Users who need more control will use systemctl directly.
for unit in $(systemctl list-unit-files --full --type=socket 2>/dev/null | sed -ne 's/\.socket\s*[a-z]*\s*$/.socket/p'); do
if [ "$(systemctl -p Triggers show $unit)" = "Triggers=${UNIT}" ]; then
systemctl $sctl_args ${ACTION} $unit
fi
done
exec systemctl $sctl_args ${ACTION} ${UNIT}
;;
reload)
_canreload="$(systemctl -p CanReload show ${UNIT} 2>/dev/null)"
if [ "$_canreload" = "CanReload=no" ]; then
# The reload action falls back to the sysv init script just in case
# the systemd service file does not (yet) support reload for a
# specific service.
run_via_sysvinit
else
exec systemctl $sctl_args reload "${UNIT}"
fi
;;
force-stop)
exec systemctl --signal=KILL kill "${UNIT}"
;;
force-reload)
_canreload="$(systemctl -p CanReload show ${UNIT} 2>/dev/null)"
if [ "$_canreload" = "CanReload=no" ]; then
exec systemctl $sctl_args restart "${UNIT}"
else
exec systemctl $sctl_args reload "${UNIT}"
fi
;;
*)
# We try to run non-standard actions by running
# the init script directly.
run_via_sysvinit
;;
esac
fi

update_openrc_started_symlinks
run_via_sysvinit

上面是service脚本的源码,每个linux里面都应该是一样的。
第一个函数

1
2
3
4
5
6
7
8
is_ignored_file() {
case "$1" in
skeleton | README | *.dpkg-dist | *.dpkg-old | rc | rcS | single | reboot | bootclean.sh)
return 0
;;
esac
return 1
}

作用就只是,检查在执行脚本的时候,传入的第一个参数是否为,应该被忽略的文件。
下一个部分,

1
2
3
4
5
6
7
8
VERSION="`basename $0` ver. 1.57+kali1"
USAGE="Usage: `basename $0` < option > | --status-all | \
[ service_name [ command | --full-restart ] ]"
SERVICE=
ACTION=
SERVICEDIR="/etc/init.d"
OPTIONS=
is_systemd=

只是普通的变量设置和初始化。
下面这一串,是检查是否输入参数,没输入就弹出使用方法。下一个是检查systemd服务是否存在,存在就设置is_systemd=1

1
2
3
4
5
6
7
8
if [ $# -eq 0 ]; then
echo "${USAGE}" >&2
exit 1
fi

if [ -d /run/systemd/system ]; then
is_systemd=1
fi

之后,是这个大循环,判断是只要传入了参数(其实是,没有处理的参数个数),就会进入循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
while [ $# -gt 0 ]; do

if [ -z "${SERVICE}" -a $# -eq 1 -a "${1}" = "--status-all" ]; then
cd ${SERVICEDIR}
for SERVICE in * ; do
case "${SERVICE}" in
functions | halt | killall | single| linuxconf| kudzu)
;;
*)
if ! is_ignored_file "${SERVICE}" \
&& [ -x "${SERVICEDIR}/${SERVICE}" ]; then
out=$(env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" status 2>&1)
retval=$?
if echo "$out" | egrep -iq "usage:"; then
#printf " %s %-60s %s\n" "[?]" "$SERVICE:" "unknown" 1>&2
echo " [ ? ] $SERVICE" 1>&2
continue
else
if [ "$retval" = "0" -a -n "$out" ]; then
#printf " %s %-60s %s\n" "[+]" "$SERVICE:" "running"
echo " [ + ] $SERVICE"
continue
else
#printf " %s %-60s %s\n" "[-]" "$SERVICE:" "NOT running"
echo " [ - ] $SERVICE"
continue
fi
fi
#env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" status
fi
;;
esac
done
exit 0
elif [ $# -eq 2 -a "${2}" = "--full-restart" ]; then
SERVICE="${1}"
# On systems using systemd, we just perform a normal restart:
# A restart with systemd is already a full restart.
if [ -n "$is_systemd" ]; then
ACTION="restart"
else
if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" stop
env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" start
exit $?
fi
fi
elif [ -z "${SERVICE}" ]; then
SERVICE="${1}"
elif [ -z "${ACTION}" ]; then
ACTION="${1}"
else
OPTIONS="${OPTIONS} ${1}"
fi
shift
;;
esac
done

下面拆出来第一个if的部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
if [ -z "${SERVICE}" -a $# -eq 1 -a "${1}" = "--status-all" ]; then
cd ${SERVICEDIR}
for SERVICE in * ; do
case "${SERVICE}" in
functions | halt | killall | single| linuxconf| kudzu)
;;
*)
if ! is_ignored_file "${SERVICE}" \
&& [ -x "${SERVICEDIR}/${SERVICE}" ]; then
out=$(env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" status 2>&1)
retval=$?
if echo "$out" | egrep -iq "usage:"; then
#printf " %s %-60s %s\n" "[?]" "$SERVICE:" "unknown" 1>&2
echo " [ ? ] $SERVICE" 1>&2
continue
else
if [ "$retval" = "0" -a -n "$out" ]; then
#printf " %s %-60s %s\n" "[+]" "$SERVICE:" "running"
echo " [ + ] $SERVICE"
continue
else
#printf " %s %-60s %s\n" "[-]" "$SERVICE:" "NOT running"
echo " [ - ] $SERVICE"
continue
fi
fi
#env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" status
fi
;;
esac
done
exit 0

这个if的进入的条件是,对service变量进行了设置,且只有一个参数未处理,且这个参数为“–status-all”。之后就打开工作目录,即/etc/init.d/,之后是个case语句。

1
2
3
case "${SERVICE}" in
functions | halt | killall | single| linuxconf| kudzu)
;;

如果service的值是上面的这些,就跳过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
*)
if ! is_ignored_file "${SERVICE}" \
&& [ -x "${SERVICEDIR}/${SERVICE}" ]; then
out=$(env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" status 2>&1)
retval=$?
if echo "$out" | egrep -iq "usage:"; then
#printf " %s %-60s %s\n" "[?]" "$SERVICE:" "unknown" 1>&2
echo " [ ? ] $SERVICE" 1>&2
continue
else
if [ "$retval" = "0" -a -n "$out" ]; then
#printf " %s %-60s %s\n" "[+]" "$SERVICE:" "running"
echo " [ + ] $SERVICE"
continue
else
#printf " %s %-60s %s\n" "[-]" "$SERVICE:" "NOT running"
echo " [ - ] $SERVICE"
continue
fi
fi

判断service的值是否是is_ignored_file函数所定义的,且判断,$service的值,是否在/etc/init.d/下,并且要可执行。如果都正确,就调用env,并把输出定向到标准输出上。
然后后面的,就是对运行状况进行判断,并输出对应信息。

1
env -i ${SERVICEDIR}/${SERVICE}

然后后面的,就是对运行状况进行判断,并输出对应信息。最后exit 0,退出此脚本。
下面

1
2
3
4
5
6
7
8
9
10
11
12
13
elif [ $# -eq 2 -a "${2}" = "--full-restart" ]; then
SERVICE="${1}"
# On systems using systemd, we just perform a normal restart:
# A restart with systemd is already a full restart.
if [ -n "$is_systemd" ]; then
ACTION="restart"
else
if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" stop
env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" start
exit $?
fi
fi

判断,未处理的参数为两个,且第二个参数为“–full-restart”,就进入此循环。
设置service的值为第一个传入的参数。之后判断,之前$is_systemd的值是否设置,设置过了,就将ACTION设为“restart”,进入下一个循环。最后继续判断,$service的值,是否在/etc/init.d/下,并且要可执行。然后执行env,将服务stop后再start,之后退出脚本。
再下面

1
2
3
4
5
6
7
8
9
10
11
elif [ -z "${SERVICE}" ]; then
SERVICE="${1}"
elif [ -z "${ACTION}" ]; then
ACTION="${1}"
else
OPTIONS="${OPTIONS} ${1}"
fi
shift
;;
esac
done

这边就是对变量的赋值阶段,如果$service为空,就吧第一个参数赋值给他,然后shift,即所有参数向前移动一位(例如$2变为$1,而$1的值就直接丢弃)开始下一轮循环,下面其他的判断同理。最后当所有参数都处理完了,就会退出。
之后,确实会存在,在while里,只进行了变量赋值,没有对应执行的情况。那就会在下面通过其他方法执行。

下面会执行的是,调用systemctl来启动相应的服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# When this machine is running systemd, standard service calls are turned into
# systemctl calls.
if [ -n "$is_systemd" ]
then
UNIT="${SERVICE%.sh}.service"
# avoid deadlocks during bootup and shutdown from units/hooks
# which call "invoke-rc.d service reload" and similar, since
# the synchronous wait plus systemd's normal behaviour of
# transactionally processing all dependencies first easily
# causes dependency loops
if ! systemctl --quiet is-active multi-user.target; then
sctl_args="--job-mode=ignore-dependencies"
fi

case "${ACTION}" in
restart|status|try-restart)
exec systemctl $sctl_args ${ACTION} ${UNIT}
;;
start|stop)
# Follow the principle of least surprise for SysV people:
# When running "service foo stop" and foo happens to be a service that
# has one or more .socket files, we also stop the .socket units.
# Users who need more control will use systemctl directly.
for unit in $(systemctl list-unit-files --full --type=socket 2>/dev/null | sed -ne 's/\.socket\s*[a-z]*\s*$/.socket/p'); do
if [ "$(systemctl -p Triggers show $unit)" = "Triggers=${UNIT}" ]; then
systemctl $sctl_args ${ACTION} $unit
fi
done
exec systemctl $sctl_args ${ACTION} ${UNIT}
;;
reload)
_canreload="$(systemctl -p CanReload show ${UNIT} 2>/dev/null)"
if [ "$_canreload" = "CanReload=no" ]; then
# The reload action falls back to the sysv init script just in case
# the systemd service file does not (yet) support reload for a
# specific service.
run_via_sysvinit
else
exec systemctl $sctl_args reload "${UNIT}"
fi
;;
force-stop)
exec systemctl --signal=KILL kill "${UNIT}"
;;
force-reload)
_canreload="$(systemctl -p CanReload show ${UNIT} 2>/dev/null)"
if [ "$_canreload" = "CanReload=no" ]; then
exec systemctl $sctl_args restart "${UNIT}"
else
exec systemctl $sctl_args reload "${UNIT}"
fi
;;
*)
# We try to run non-standard actions by running
# the init script directly.
run_via_sysvinit
;;
esac
fi

首先,还是判断$is_systemd是否设置,进入之后,将UNIT赋值为”${SERVICE%.sh}.service”,之后对action内容进行选择,分别执行不同的参数。

再下面是试图通过链接方式,启动服务。
调用的是函数update_openrc_started_symlinks,即

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
update_openrc_started_symlinks() {
# maintain the symlinks of /run/openrc/started so that
# rc-status works with the service command as well
if [ -d /run/openrc/started ] ; then
case "${ACTION}" in
start)
if [ ! -h /run/openrc/started/$SERVICE ] ; then
ln -s $SERVICEDIR/$SERVICE /run/openrc/started/$SERVICE || true
fi
;;
stop)
rm /run/openrc/started/$SERVICE || true
;;
esac
fi
}

这个使用openrc组件,用链接将$SERVICEDIR/$SERVICE这个文件,链接到/run/openrc/started/$SERVICE,作为启动,stop就是删除这个链接。用openrc最为方法启动。这个组件,ubuntu和kali都没有预装。

最后就是run_via_sysvinit

1
2
3
4
5
6
7
8
9
run_via_sysvinit() {
# Otherwise, use the traditional sysvinit
if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
exec env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" ${ACTION} ${OPTIONS}
else
echo "${SERVICE}: unrecognized service" >&2
exit 1
fi
}

作用非常简单,就是判断${SERVICEDIR}/${SERVICE}是否存在,且可执行,就直接运行。

最后的最后,之前那个sixes靶机最后的exp

1
sudo service ../../bin/bash

就是首先,调用while,将service赋值为../../bin/bash,之后就退出while循环,在run_via_sysvinit()函数里执行

1
env -i /etc/init.d/../../bin/bash

由于是用root身份执行的,就弹出了root的shell。
这个就是利用了,之前在while循环中,对$service赋值时,并没有对目录穿越的../进行过滤,从而可以通过这种方式,就可以执行任意一个sh脚本。